import { isObject } from './object'

function delay(wait) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, wait)
  })
}

/**
 * 防抖函数，当调用动作触发一段时间后，延迟执行，若在这段时间间隔内又调用此动作则将重新计算时间间隔。
 *
 * @param {Function} func
 * @param {Object} [options={}]
 * @param {boolean} [options.leading=true] 指定在延迟开始前调用，默认false
 * @param {number} [options.maxWait] 设置 func 允许被延迟的最大值
 * @param {boolean} [options.trailing=true] 指定在延迟结束后调用，默认true
 */
function debounce(func, wait, options) {
  let lastArgs
  let lastThis
  let maxWait
  let result
  let timerId
  let lastCallTime

  let lastInvokeTime = 0
  let leading = false
  let maxing = false
  let trailing = true

  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }

  wait = +wait || 0

  if (isObject(options)) {
    leading = !!options.leading
    maxing = 'maxWait' in options
    maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
    trailing = 'trailing' in options ? !!options.trailing : trailing
  }

  function invokeFunc(time) {
    const args = lastArgs
    const thisArg = lastThis

    lastArgs = lastThis = undefined
    lastInvokeTime = time
    result = func.apply(thisArg, args)

    return result
  }

  function startTimer(pendingFunc, waitTime) {
    return setTimeout(pendingFunc, waitTime)
  }

  function cancelTimer(id) {
    clearTimeout(id)
  }

  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time
    // Start the timer for the trailing edge.
    timerId = startTimer(timerExpired, wait)

    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result
  }

  function remainingWait(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime
    const timeWaiting = wait - timeSinceLastCall

    return maxing
      ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
      : timeWaiting
  }

  function shouldInvoke(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return (
      lastCallTime === undefined ||
      timeSinceLastCall >= wait ||
      timeSinceLastCall < 0 ||
      (maxing && timeSinceLastInvoke >= maxWait)
    )
  }

  function timerExpired() {
    const time = Date.now()

    if (shouldInvoke(time)) {
      return trailingEdge(time)
    }

    // Restart the timer.
    timerId = startTimer(timerExpired, remainingWait(time))
  }

  function trailingEdge(time) {
    timerId = undefined

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time)
    }

    lastArgs = lastThis = undefined

    return result
  }

  function cancel() {
    if (timerId !== undefined) {
      cancelTimer(timerId)
    }

    lastInvokeTime = 0
    lastArgs = lastCallTime = lastThis = timerId = undefined
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(Date.now())
  }

  function pending() {
    return timerId !== undefined
  }

  function debounced(...args) {
    const time = Date.now()
    const isInvoking = shouldInvoke(time)

    lastArgs = args
    lastThis = this
    lastCallTime = time

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime)
      }

      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = startTimer(timerExpired, wait)

        return invokeFunc(lastCallTime)
      }
    }

    if (timerId === undefined) {
      timerId = startTimer(timerExpired, wait)
    }

    return result
  }

  debounced.cancel = cancel
  debounced.flush = flush
  debounced.pending = pending

  return debounced
}

/**
 * 节流函数， 每 `wait` ms执行一次
 *
 * @param {Function} func
 * @param {number} [wait=0] milliseconds
 * @param {Object} [options={}]
 * @param {boolean} [options.leading=true] 指定调用在节流开始前，默认true
 * @param {boolean} [options.trailing=true] 指定调用在节流结束后，默认true
 */
function throttle(func, wait, options) {
  let leading = true
  let trailing = true

  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }

  if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading
    trailing = 'trailing' in options ? !!options.trailing : trailing
  }

  return debounce(func, wait, {
    leading,
    trailing,
    maxWait: wait
  })
}

/**
 * 将参数格式化后拼接在url上
 *
 * 值为对象则自动用JSON.stringify处理
 * 值为undefined、null与空字符串时移除
 * @param {路径, String} url
 * @param {参数，Object} params
 */
const getUrlWithParams = (url, params) => {
  if (!params || JSON.stringify(params) === '{}') {
    return url
  }

  const list = []

  Object.keys(params).forEach(key => {
    let item = params[key]

    if (typeof item === 'object') {
      item = JSON.stringify(item)
    }

    if (item !== undefined && item !== 'null' && item !== '') {
      list.push(`${key}=${item}`)
    }
  })

  return list.length > 0 ? `${url}?${list.join('&')}` : url
}

const getParamsFromUrl = () => {
  const querystring = {}
  const url = window.decodeURIComponent(window.location.href)
  const queryStr = url.replace(/^[^?#]*\??/g, '')

  queryStr.replace(/([^=&?#]+)=([^&]*)/g, (a, b, c) => {
    b = decodeURIComponent(b)
    c = decodeURIComponent(c)

    querystring[b] = c
  })

  return querystring
}

const isMobileNumber = str => /^1[3,4,5,6,7,8,9]\d{9}$/.test(str)
const isPersonName = str => /^[a-zA-Z\u4e00-\u9fa5 ]+$/.test(str)

const getQRCodeParams = function (scene) {
  const str = decodeURIComponent(scene)
  const params = {}

  str.split('&').forEach(item => {
    const [key, value] = item.split('=')

    params[key] = value
  })

  return params
}

// 保留2位小数，如：2，会在2后面补上00.即2.00
function toDecimal(x, n = 0) {
  let f = parseFloat(x)

  if (isNaN(f)) {
    return false
  }

  f = Math.round(x * 100) / 100

  let s = f.toString()
  let rs = s && s.indexOf('.')

  if (rs < 0) {
    rs = s.length
    s += '.'
  }

  while (s.length <= rs + n) {
    s += '0'
  }

  return s
}

function splitFloatNumber(num) {
  const decimal = toDecimal(num, 2)

  return decimal.split('.') || []
}

/**
 * 格式化文件大小, 输出成带单位的字符串
 * @param {Number} size 文件大小
 * @param {Number} [pointLength=2] 精确到的小数点数。
 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。
 *    如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
 */
function formatSize(size, pointLength, units) {
  let unit
  units = units || ['B', 'K', 'M', 'G', 'TB']
  while ((unit = units.shift()) && size > 1024) {
    size = size / 1024
  }
  return (
    (unit === 'B'
      ? size
      : size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit
  )
}

export const userAgent = navigator.userAgent

export const deviceAgent = `okhttp/3.11.0 ${navigator.userAgent}`

/**
 * @name 判断iOS
 */
export const isIOS = () => {
  let iOs = !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) //ios终端\
  return iOs
}
/**
 * @name 判断android
 */
export const isAndroid = () => {
  let android = userAgent.indexOf('Android') > -1 || u.indexOf('Adr') > -1 //android终端
  return android
}
export const checkPlatform = () => {
  const phoneInfo = {}
  let appSystem = 'Others'
  let appSystemVersion = 'Others'
  if (/android/i.test(navigator.userAgent)) {
    appSystem = 'Android'
  }
  if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
    appSystem = 'IOS'
  }
  if (appSystem == 'Android') {
    appSystemVersion =
      navigator.appVersion
        .match(/Android\s(\d{0,9}\.?\d{0,9}\.?\d{0,9})/gi)[0]
        .split(' ')[1] || 'Others'
  }
  if (appSystem == 'IOS') {
    appSystemVersion =
      navigator.appVersion
        .match(/iP.*;.*\sOS\s(\d+_?\d+_?\d{0,9})/gi)[0]
        .split(' ')[4] || 'Others'
  }
  phoneInfo.appSystem = appSystem
  phoneInfo.appSystemVersion = appSystemVersion
  return phoneInfo
}
export {
  delay,
  debounce,
  throttle,
  getUrlWithParams,
  getParamsFromUrl,
  isMobileNumber,
  isPersonName,
  getQRCodeParams,
  toDecimal,
  splitFloatNumber,
  formatSize
}
